using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SaveTheQueen.Scene;
using SaveTheQueen.Utils;

namespace SaveTheQueen
{
    public class Core : Microsoft.Xna.Framework.Game
    {
        #region StaticMembers
        private static Core instance;
        private static Matrix scaleMatrix;
        private static int screenX;
        private static int screenY;

        public static Core Instance
        {
            get { return Core.instance; }
        }
        public static Matrix ScaleMatrix
        {
            get { return Core.scaleMatrix; }
        }
        public static int ScreenX
        {
            get { return Core.screenX; }
            set { ChangeResolution(value); }
        }
        public static int ScreenY
        {
            get { return Core.screenY; }
        }

        private static void ChangeResolution(int value)
        {
            screenX = value;
            screenY = (screenX * 9) / 16;

            Instance.graphics.PreferredBackBufferWidth = screenX;
            Instance.graphics.PreferredBackBufferHeight = screenY;
            Instance.graphics.ApplyChanges(); // this calls the Load for every component, so the matrix is updated on the load content method.
        }
        private static void ComputeScaleMatrix(int screenWidth)
        {
            float scalefactor = screenWidth / 800f;

            // scale on Z gives problem with sprite depth;
            scaleMatrix = Matrix.CreateScale(scalefactor, scalefactor, 1f);
        }
        #endregion

        protected GraphicsDeviceManager graphics;
        protected SpriteBatch spriteBatch;
        protected bool paused;

        protected BaseScene scene;
        private RenderTarget2D target;

        public Core()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            Core.instance = this;
            Audio.LoadAudio(this);

            graphics.PreferMultiSampling = QualityConfig.Instance.AntiAliasing;
            graphics.PreferredBackBufferWidth = QualityConfig.Instance.ResolutionX;
            graphics.PreferredBackBufferHeight = QualityConfig.Instance.ResolutionY;
            graphics.SynchronizeWithVerticalRetrace = QualityConfig.Instance.VSync;
            graphics.IsFullScreen = QualityConfig.Instance.FullScreen;

            ScreenX = 800;
            ComputeScaleMatrix(ScreenX);

            scene = new NullScene(this);
            this.Components.Add(scene);

            paused = false;
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Services.AddService(typeof(SpriteBatch), spriteBatch);

            SpriteManager.LoadContents(Content);

            target = new RenderTarget2D(GraphicsDevice, ScreenX, ScreenY);

            ComputeScaleMatrix(ScreenX);
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            MouseInput.Pool();
            KeyboardInput.Pool();
            GamepadInput.Pool();
            if (KeyboardInput.Paused == Button.Down)
            {
                paused = !paused; // if true, becomes false, if false, becomes true
            }

            #region Update
            if (!paused)
            {
                if (KeyboardInput.SpeedUp == Button.Held)
                {
                    GameTime gtFF = new GameTime(gameTime.TotalGameTime, new TimeSpan(gameTime.ElapsedGameTime.Ticks * 2));
                    base.Update(gtFF);
                }
                else if (KeyboardInput.SpeedDown == Button.Held)
                {
                    GameTime gtFF = new GameTime(gameTime.TotalGameTime, new TimeSpan(gameTime.ElapsedGameTime.Ticks / 2));
                    base.Update(gtFF);
                }
                else
                {
                    base.Update(gameTime);
                }
            }
            #endregion
        }
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.SetRenderTarget(target);
            GraphicsDevice.Clear(Color.Black);

            // Find right speed and then launch the Render method.
            #region Draw
            if (KeyboardInput.SpeedUp == Button.Held)
            {
                GameTime gtFF = new GameTime(gameTime.TotalGameTime, new TimeSpan(gameTime.ElapsedGameTime.Ticks * 2));
                Render(gtFF);
            }
            else if (KeyboardInput.SpeedDown == Button.Held)
            {
                GameTime gtFF = new GameTime(gameTime.TotalGameTime, new TimeSpan(gameTime.ElapsedGameTime.Ticks / 2));
                Render(gtFF);
            }
            else
            {
                Render(gameTime);
            }
            #endregion
        }

        private void Render(GameTime gameTime)
        {
            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, scaleMatrix);
            base.Draw(gameTime); // Draw Scene at Back Buffer
            spriteBatch.End();

            graphics.GraphicsDevice.SetRenderTarget(null);
            scene.PostProcess(target, spriteBatch, gameTime); // Post Process the Back Buffer and draw it at the screen.
        }
        public static void ChangeScene(BaseScene nextScene)
        {
            if (nextScene != null)
            {
                Instance.Components.Clear();

                Instance.scene = nextScene;
                Instance.Components.Add(Instance.scene);

                System.Diagnostics.Debug.WriteLine("Change Scene done!");
            }
            else
                Instance.Exit();
        }
    }
}
